import 'dart:typed_data';
import 'package:binary/binary.dart';
import 'package:bluetooth_parse_plugin/bluetooth_parse_plugin.dart';
import 'package:bluetooth_parse_plugin/src/base/message_base.dart';
import 'package:bluetooth_parse_plugin/src/common/enums.dart';
import 'package:bluetooth_parse_plugin/src/extension/int_extension.dart';
import 'package:bluetooth_parse_plugin/src/lorin/lorin_constant.dart';
import 'package:bluetooth_parse_plugin/src/lorin/lorin_enum.dart';
import 'package:bluetooth_parse_plugin/src/lorin/lorin_data.dart';
import 'package:bluetooth_parse_plugin/src/lorin/lorin_instruction_enum.dart';
import 'package:bluetooth_parse_plugin/src/lorin/lorin_tab.dart';
import 'package:bluetooth_parse_plugin/src/utils/utils.dart';
import 'package:flutter/foundation.dart';

// 消息
class LorinMessage extends MessageBase {
  // 消息头
  static final Uint8List head =
      Uint8List.fromList([LorinConstant.HEAD1, LorinConstant.HEAD2]);

  // 帧主体：bit7：固定为0，bit6是否包含Tab，bit5-4校验方式，bit3是否加密，bit2-1-0数据格式
  late Uint8 format;

  // 是否需要加密
  late bool needCrypto;

  // format中bit3=1表示存在：每次通信随机一个0~255的值，作为密钥索引，报文中“Tab”开始到最后一字节的校验值都需要加密
  late Uint8? key;

  // 是否需要Tab
  late bool needTab;

  // format中bit6=1表示存在：包标签：在一次通讯过程中，它由主动发起数据请求的一方生成，接收方收到后如需应答则应答的数据帧必须携带同样的标签
  late LorinTab? tab;

  // data字段长度
  late Uint8 length;

  // 数据格式
  late LorinDataFormatType dataFormatType;

  // 数据段
  late LorinData? data;

  // 校验方式  0：none 无校验、1：parity 奇偶校验、2：crc16 CRC16_MODBUS标准计算、3：crc32 //CRC32计算
  late MessageCheckType checkType;

  // format中bit3=1表示存在：校验值(format-check的校验）
  late Uint8List? check;

  // 从LorinIntructionEnum中解析 LorinMessage
  LorinMessage.from(
    LorinIntructionEnum intructionEnum, {
    Uint8List? tabList,
  }) {
    // 数据格式
    dataFormatType = intructionEnum.format;
    // 数据段
    LorinData data = LorinData(
      dataFormatType: dataFormatType,
      sourceDeviceNum: intructionEnum.sourceDevice,
      targetDeviceNum: intructionEnum.targetDevice,
      channelType: intructionEnum.channel,
      propertyType: intructionEnum.property,
      cmd: intructionEnum.cmdList,
      para: intructionEnum.param,
    );
    debugPrint("LorinData data = $data");
    // 是否需要加密
    needCrypto = LorinCrypto().needCrypto;
    // 随机数
    if (needCrypto) key = Uint8(Utils.random(0, 255));
    // tab标签
    needTab = tabList != null;
    if (needTab) tab = LorinTab(tabList!);
    // 数据段长度
    length = Uint8(data.dataLength());
    // 校验类型
    var checkType = MessageCheckType.parity;
    // 计算format
    getFormat();
    // 计算check
    check = checkType.checkBytes(_getNeedCheckList());
  }

  // 从Uint8List中解析 LorinMessage
  LorinMessage.fromList(Uint8List uint8list) {
    if (uint8list.length >= minLength() &&
        uint8list[0] == LorinConstant.HEAD1 &&
        uint8list[1] == LorinConstant.HEAD2) {
      int headLength = 2;
      int formatLength = 1;
      int lengthLength = 1;
      format = Uint8(uint8list[headLength]);
      // 是否加密
      needCrypto = _rspNeedCrypto();
      // 是否有Tab
      needTab = _rspNeedTab();
      // 校验方式
      checkType = _rspCheckType();
      // 数据段格式（标准组/专用组）
      dataFormatType = _rspDataFormatType();
      int keyLength = needCrypto ? 1 : 0;
      int tabLength = needTab ? tab!.dataLength() : 0;
      // 如果加密，获取随机值（目前默认没有加密）
      if (needCrypto) key = Uint8(uint8list[headLength + formatLength]);
      if (needTab) tab = LorinTab(getTab(uint8list));
      // 数据段长度
      length =
          Uint8(uint8list[headLength + formatLength + keyLength + tabLength]);
      int dataStartIndex =
          headLength + formatLength + keyLength + tabLength + lengthLength;
      // 数据段
      Uint8List dataList = Uint8List.sublistView(
          uint8list, dataStartIndex, dataStartIndex + length.value);
      data = LorinData(
        dataFormatType: dataFormatType,
        sourceDeviceNum: getSourceDevcieNum(dataList),
        targetDeviceNum: getTargetDevcieNum(dataList),
        channelType: getChannelType(dataList),
        propertyType: getPropertyType(dataList),
        cmd: getCmd(dataList),
        para: getPara(dataList),
      );

      // 提取check
      int checkStartIndex = headLength +
          formatLength +
          keyLength +
          tabLength +
          lengthLength +
          dataList.length;
      check = Uint8List.sublistView(uint8list, checkStartIndex,
          checkStartIndex + checkType.checkLength());
      if (!checkResult()) {
        throw Exception("【$runtimeType】 校验失败");
      }
    } else {
      throw Exception("【$runtimeType】 数据格式错误");
    }
  }

  @override
  String msgIdentify() {
    return tab?.toBytes.toString() ?? "";
  }

  @override
  Uint8List toBytes() {
    final builder = BytesBuilder();
    builder.add(head);
    builder.addByte(format.value);
    if (needCrypto) builder.addByte(key!.value);
    var tabBytes = needTab ? tab?.toBytes() : null;
    if (tabBytes != null) builder.add(tabBytes);
    builder.addByte(length.value);
    var dataBytes = data?.toBytes();
    if (dataBytes != null) builder.add(dataBytes);
    if (check != null && checkType != MessageCheckType.none) {
      builder.add(check!);
    }
    return builder.toBytes();
  }

  @override
  int totalLength() {
    return toBytes().length;
  }

  @override
  bool checkResult() {
    Uint8List calCheck = checkType.checkBytes(_getNeedCheckList());
    // 返回数据中的获取check值
    if (check == null) {
      return calCheck.isEmpty ? true : false;
    } else {
      return Utils.equals(check!, calCheck);
    }
  }

  // 最小长度
  int minLength() => 4;
}

// 从LorinMessage响应数据中提取数据
extension LorinMessageRspExtension on LorinMessage {
  // 提取Tab
  Uint8List getTab(Uint8List data) {
    if (needTab) {
      return tab?.toBytes() ?? Uint8List(0);
    }
    return Uint8List(0);
  }

  // 提取Format
  Uint8 getFormat() {
    // 计算format
    int temp = 0;
    if (needTab) temp += (needTab ? 1 : 0) << 6;
    temp += (checkType.rawValue >> 1 & 0x01) << 5;
    temp += (checkType.rawValue & 0x01) << 4;
    if (needCrypto) temp += (needCrypto ? 1 : 0) << 3;
    temp += (dataFormatType.rawValue >> 2 & 0x01) << 2;
    temp += (dataFormatType.rawValue >> 1 & 0x01) << 1;
    temp += (dataFormatType.rawValue & 0x01);
    return Uint8(temp);
  }

  // 提取源设备ID
  int? getSourceDevcieNum(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.norStcpp:
      case LorinDataFormatType.speStcp:
        return data.first;
      default:
        return null;
    }
  }

  // 提取目标设备ID
  int? getTargetDevcieNum(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.speTcp:
      case LorinDataFormatType.norTcpp:
        return data[0];
      case LorinDataFormatType.speStcp:
      case LorinDataFormatType.norStcpp:
        return data[1];
      default:
        return null;
    }
  }

  // 提取通道类型
  LorinDataChannelType? getChannelType(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.norCpp:
        return LorinDataChannelType.getTypeByNum(data[0].bit(0, length: 6));
      case LorinDataFormatType.norTcpp:
        return LorinDataChannelType.getTypeByNum(data[1].bit(0, length: 6));
      case LorinDataFormatType.norStcpp:
        return LorinDataChannelType.getTypeByNum(data[2].bit(0, length: 6));
      default:
        return null;
    }
  }

  // 提取通道属性
  LorinDataPropertyType? getPropertyType(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.norCpp:
        return LorinDataPropertyType.getTypeByNum(data[1].bit(0, length: 7));
      case LorinDataFormatType.norTcpp:
        return LorinDataPropertyType.getTypeByNum(data[2].bit(0, length: 7));
      case LorinDataFormatType.norStcpp:
        return LorinDataPropertyType.getTypeByNum(data[3].bit(0, length: 7));
      default:
        return null;
    }
  }

  // 提取cmd
  Uint8List? getCmd(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.speCp:
      case LorinDataFormatType.speTcp:
      case LorinDataFormatType.speStcp:
        List<int> cmd = [];
        var cmd1 = data.first;
        /*/ 是否为应答包
        if ((cmd1 >> 7) & 0x01 == 1) {
          // cmd是否有扩展
          if ((cmd1 >> 6) & 0x01 == 1) {
            // 有扩展
          } else {
            // 无扩展
            cmd.add(cmd1);
          }
        }
        */
        // 无扩展
        cmd.add(cmd1);
        return Uint8List.fromList(cmd);
      default:
        return null;
    }
  }

  // 提取para
  Uint8List getPara(Uint8List data) {
    switch (dataFormatType) {
      case LorinDataFormatType.speCp:
      case LorinDataFormatType.speTcp:
      case LorinDataFormatType.speStcp:
        var cmdLength = getCmd(data)?.length ?? 0;
        // 除去cmd剩下的数据
        List<int> list = [];
        for (var i = cmdLength; i < data.length; i++) {
          list.add(data[i]);
        }
        return Uint8List.fromList(list);
      default:
        List<int> list = [];
        for (var i = dataFormatType.rspExcludingParaLen();
            i < data.length;
            i++) {
          list.add(data[i]);
        }
        return Uint8List.fromList(list);
    }
  }

  // 是否需要加密
  bool _rspNeedCrypto() {
    return (format.value >> 3 & 0x01) == 1;
  }

  // 是否需要Tab(由format中bit6决定)
  bool _rspNeedTab() {
    return (format.value >> 6 & 0x01) == 1;
  }

  // 校验方式(由format中bit5-4决定)
  MessageCheckType _rspCheckType() {
    return MessageCheckType.getTypeByNum(format.value >> 4 & 0x03) ??
        MessageCheckType.none;
  }

  // 数据格式
  LorinDataFormatType _rspDataFormatType() {
    return LorinDataFormatType.getTypeByNum(format.value & 0x07) ??
        LorinDataFormatType.none;
  }

  // 需要计算check的数组
  Uint8List _getNeedCheckList() {
    List<int> checkList = [];
    checkList.add(format.value);
    if (needCrypto && key != null) checkList.add(key!.value);
    if (needTab) {
      var tabBytes = tab?.toBytes();
      if (tabBytes != null) checkList.addAll(tabBytes);
    }
    checkList.add(length.value);
    var dataBytes = data?.toBytes();
    if (data != null && dataBytes != null) checkList.addAll(dataBytes);
    return Uint8List.fromList(checkList);
  }
}
